// SPDX-FileCopyrightText: Copyright 2025-2025 深圳市同心圆网络有限公司
// SPDX-License-Identifier: GPL-3.0-only

package local_store_impl

import (
	"fmt"
	"io"
	"io/fs"
	"os"
	"path/filepath"
	"strings"
	"time"

	"gitcode.com/openseaotter/so_server/storage/core"
	"gitcode.com/openseaotter/so_server/utils"
)

type _LocalStoreImpl struct{}

func NewLocalStoreImpl() core.BlobStore {
	return &_LocalStoreImpl{}
}

func (storeImpl *_LocalStoreImpl) calcStoreDir(digest string) (string, error) {
	pos := strings.Index(digest, ":")
	if pos == -1 {
		return "", fmt.Errorf("wrong digest")
	}
	digestContent := digest[pos+1:]
	if len(digestContent) < 8 {
		return "", fmt.Errorf("wrong digest")
	}
	part1 := digestContent[0:2]
	part2 := digestContent[2:4]
	part3 := digestContent[4:6]
	part4 := digestContent[6:8]

	return filepath.Join(utils.DATA_DIR_PATH, "blobs", part1, part2, part3, part4), nil
}

func (storeImpl *_LocalStoreImpl) Length(digest string) (int64, error) {
	digest = strings.ToLower(digest)

	dirPath, err := storeImpl.calcStoreDir(digest)
	if err != nil {
		return 0, err
	}
	filePath := filepath.Join(dirPath, digest)
	stat, err := os.Stat(filePath)
	if err != nil {
		return 0, err
	}
	return stat.Size(), nil
}

func (storeImpl *_LocalStoreImpl) Open(digest string) (io.ReadSeekCloser, error) {
	digest = strings.ToLower(digest)

	dirPath, err := storeImpl.calcStoreDir(digest)
	if err != nil {
		return nil, err
	}
	filePath := filepath.Join(dirPath, digest)
	return os.Open(filePath)
}

func (storeImpl *_LocalStoreImpl) Save(srcFile io.Reader, digest string) error {
	digest = strings.ToLower(digest)

	dirPath, err := storeImpl.calcStoreDir(digest)
	if err != nil {
		return err
	}
	os.MkdirAll(dirPath, 0755) //skip error check
	filePath := filepath.Join(dirPath, digest)
	destFile, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
	if err != nil {
		return err
	}
	defer destFile.Close()

	_, err = io.Copy(destFile, srcFile)
	return err
}

func (storeImpl *_LocalStoreImpl) Remove(digest string) error {
	digest = strings.ToLower(digest)

	dirPath, err := storeImpl.calcStoreDir(digest)
	if err != nil {
		return err
	}
	filePath := filepath.Join(dirPath, digest)
	return os.Remove(filePath)
}

func (storeImpl *_LocalStoreImpl) Walk(dayBefore uint, callBack func(blobId string)) error {
	maxTime := time.Now().Unix() - int64(dayBefore)*3600*24
	rootPath := filepath.Join(utils.DATA_DIR_PATH, "blobs")

	err := filepath.Walk(rootPath, func(path string, info fs.FileInfo, err error) error {
		if err != nil || info.IsDir() {
			return nil
		}
		modTime := info.ModTime()
		if modTime.Unix() > maxTime {
			return nil
		}
		blobId := filepath.Base(info.Name())
		if len(blobId) > 3 { //skip . and ..
			callBack(blobId)
		}

		return nil
	})

	return err
}
